#!/user/bin/env python3
#-----------------------------------------------
#  重新按照面向对象的方法设计程序
# 
# 分辨率: 1920*1080
# 操作系统： ubuntu 20.04
# 浏览器： chrome， firefox
# 作者: xiedaolin 2020-9-22
#-----------------------------------------------
import json
import pathlib
import pyautogui
import fire
import random
import pytesseract
import time
from configparser import ConfigParser, _UNSET
import atexit
from threading import Timer
# **********使用Python的日志功能**********begin
import logging
from logging.handlers import TimedRotatingFileHandler

class GotConfig(ConfigParser):
    def getList(self, section, option, conv, raw=False, vars=None, fallback=_UNSET, **kwargs):
        """配置项的值转化为指定类型的列表
            conv 指定的数据类型"""
        value = self.get(section,option, raw=raw, vars=vars, fallback=fallback, **kwargs)
        if value == "": return []
        valueList = value.replace(' ','').split(',')
        convValueList = [conv(x) for x in valueList]
        return convValueList
    
    def getIntTuple(self, section, option):
        intList = self.getList(section, option, int, raw=False, vars=None, fallback=_UNSET)
        return tuple(intList)
    
    def getStrList(self, section, option):
        strList = self.getList(section, option, str, raw=False, vars=None, fallback=_UNSET)
        return strList

    def getDict(self, section, option):
        dataTypeList = self.getList(section, option, str, raw=False, vars=None, fallback="")
        if not dataTypeList: return {}
        dataTypeTupleList = [tuple(v.split(':')) for v in dataTypeList]
        return dict(dataTypeTupleList)

class BaseConfig(object):
    def __init__(self):
        self.config = self.updateParamDict()
        self.initLogger()
        self.name = self.__class__.__name__

    
    def updateParamDict(self,config = 'config.v3.ini'):
        """从配置文件中读取配置到字典对象"""
        # 创建配置文件对象
        cfg = configparser.ConfigParser()
        
        # 读取文件
        cfg.read(config, encoding='utf-8')
        para = {}
        sections = cfg.sections()
        for section in sections:
            para[section] = {}
            items = cfg.items(section)
            # 首先读取每个节点下对应配置的数据类型
            dataTypeStr = cfg.get(section, "data_type", fallback=None)
            dataTypeDict = {}
            if dataTypeStr:
                dataTypeList = dataTypeStr.replace('\n', '').replace(' ', '').split(',')
                dataTypeTupleList = [tuple(v.split(':')) for v in dataTypeList]
                dataTypeDict = dict(dataTypeTupleList)
            for item, value in items:
                if item == "data_type": continue
                #判断数据类型，采用不同的函数读取
                # dataType = cfgItemValueType.get("{}.{}".format(section,item), "iTuple")
                dataType = dataTypeDict.get(item, 'iTuple')
                if dataType =="int":
                    dictValue = int(value)
                elif dataType == "float":
                    dictValue = float(value)
                elif dataType == "bool":
                    # 下列值视为 True: '1', 'yes', 'true', 'on' 其它都是False
                    value = value.upper()
                    dictValue = True if value == 'TRUE' or value == '1' or value == 'YES' or value == 'ON' else False
                    # bool(value)
                elif dataType == "str":
                    dictValue = value
                elif dataType == "sList":
                    dictValue = value.replace(' ','').split(',')
                elif dataType == "iTuple":
                    # 逗号分隔的数字转化为元组
                    valueList = value.replace(' ','').split(',')
                    intValueList = [int(x) for x in valueList]
                    dictValue =  tuple(intValueList)
                else:
                    raise ValueError("未找到配置项对应的数据类型，无法读取对应值。配置项名称：{}.{}".format(section,item))
                
                para[section][item] = dictValue
        return para

    def initLogger(self):
        logFileName = "log/GoTAuto.log"
        self.logger = logging.getLogger("GoTAuto")
        handlerConsole = logging.StreamHandler()
        handlerFile = TimedRotatingFileHandler(filename=logFileName, when="D", interval=1)
        self.logger.setLevel(logging.DEBUG)
        handlerConsole.setLevel(logging.INFO)
        handlerFile.setLevel(logging.DEBUG)
        formatter = logging.Formatter(
            "[%(asctime)s %(name)s %(levelname)s]>>> %(message)s")
        handlerConsole.setFormatter(formatter)
        handlerFile.setFormatter(formatter)
        self.logger.addHandler(handlerConsole)
        self.logger.addHandler(handlerFile)

class Action(object):
    """所有操作动作的基类"""
    @classmethod
    def getGlobalConfig(cls, config='config.v3.ini'):
        # 创建配置文件对象
        cfg = GotConfig()        
        # 读取文件
        cfg.read(config, encoding='utf-8')
        # 全局鼠标移动的时间
        cls.duration = cfg.getfloat("Global", "duration")
        # 全局超时的时间
        cls.timeout = cfg.getfloat("Global", "timeout")
        # 每次自动化动作的间隔时间，单位秒
        cls.pause = 3.0
        # 是否要开启停战协议 有值打开，不填写False 。 下列值 True: '1', 'yes', 'true', 'on' 其它为:False
        cls.stop_battle_on = False
        # 右上角鼠标区域 造成程序暂停
        cls.pause_zone_w = 20
        cls.pause_zone_h = 20
        # 鼠标在暂停区暂停多少秒
        cls.pause_zone_sec = 30
        # 红色提示点 颜色RGB
        cls.red_dot = 228,50,50
        # 系统设置的行军队列上限
        cls.march_limit = 5
        # 派兵计划 每日 开始小时，开始分钟，结束小时，结束分钟
        cls.march_schedule = 19,30,21,30
        # 设置的派兵计划中，行军数量
        cls.march_schedule_count = 3
    def getConfig(self, config = 'config.v3.ini'):
        """从配置文件中读取配置到字典对象"""
        # 创建配置文件对象
        cfg = GotConfig()        
        # 读取文件
        cfg.read(config, encoding='utf-8')
        
        section = self.name
        para = {}
        options = cfg.options(section)
        dataTypeDict = cfg.getDict(self.name, "data_type")
        for option in options:
            if option == "data_type": continue
            #判断数据类型，采用不同的函数读取            
            dataType = dataTypeDict.get(option, 'iTuple')
            if dataType =="int":
                dictValue = cfg.getint(section, option)
            elif dataType == "float":
                dictValue = cfg.getfloat(section, option)
            elif dataType == "bool":
                dictValue = cfg.getboolean(section, option)
            elif dataType == "str":
                dictValue = cfg.get(section, option)
            elif dataType == "sList":
                dictValue = cfg.getStrList(section, option)
            elif dataType == "iTuple":
                dictValue = cfg.getIntTuple(section, option)
            else:
                raise ValueError("未找到配置项对应的数据类型，无法读取对应值。配置项名称：{}.{}".format(section,item))
            
            para[option] = dictValue
        return para
    def __init__(self, map_mode="city"):
        self.map_mode = map_mode
        self.runned = False
        self.next_runtime = 0
        self.name = self.__class__.__name__
        self.cfg = self.getConfig()
    def click(self, x=None, y=None, clicks=1, interval=0.0, button=pyautogui.PRIMARY):
        pyautogui.click(x=x, y=y, clicks=clicks, interval=interval, button=button, duration=Action.duration)
    def locateCenter(self, file, confidence=0.9, region=None):
        return pyautogui.locateCenterOnScreen( file, confidence=confidence, region=region)
    def locate(self,file, confidence=0.9, region=None):
        return pyautogui.locateOnScreen( file, confidence=confidence, region=region)
    def press(self, keys, presses=1, interval=0.0):
        pyautogui.press(keys=keys, presses=presses, interval=interval)
    def sleep(self, seconds=0):
        time.sleep(seconds)
    def scroll(self, clicks, x=None, y=None):
        pyautogui.scroll(clicks, x=x, y=y, logScreenshot=None, _pause=True, *args: Any, **kwds: Any)
    def pre_do(self):
        return True
    def do(self):
        print("do function", self.name)

        pass
    def after_do(self):
        pass
    

class ScreenWindow(Action):
    """游戏屏幕窗口相关控制"""
    def __init__(self):
        super().__init__("any")
        self.SCREEN_WIDTH, self.SCREEN_HEIGHT = pyautogui.size()
        self.SCREEN_MID_W, self.SCREEN_MID_H = int(self.SCREEN_WIDTH/2), int(self.SCREEN_HEIGHT/2)  # 屏幕中间
    def getFullScreen(self):
        """获取当前的界面是否是全屏的状态"""
        searchRegion = self.cfg['searchregion']
        findPos = pyautogui.locateOnScreen(
            "scrShot-browser/headImage.png", confidence=0.9, region=searchRegion)
        if not findPos:
            # self.logger.error("未能检测到GOT的游戏界面。")
            return False

        if findPos.left < 40 and findPos.top < 90:
            currentFullScreen = True #当前已经是全屏状态
        else:
            currentFullScreen = False
        # logger.info("获取当前的浏览器界面是否全屏状态:{}".format(currentFullScreen))
        return currentFullScreen
    def getMapMode(self):
        '''
        # 检测当前是什么地图模式【city, world】
            返回值：
                offline: 离线模式
                city: 城堡
                world: 世界地图
                loading: 切换过程中
                nothing: 未知
        '''
        currentMapMode = ""
        findOffline = self.locate('scrShot-browser/offline.png')
        if findOffline:        
            currentMapMode = "offline"
            # logger.info("当前界面的地图模式为： {}".format(currentMapMode))
            return currentMapMode

        # 校验当前的地图模式
        # 判断是否city模式
        findcity = self.locate('scrShot-browser/map_city.png', region = self.cfg['map_mode_region'])
        
        # 判断是否世界模式
        findWorld = self.locate('scrShot-browser/map_world.png', region=self.cfg['map_mode_region'])

        # 判断是否正在加载过程
        findLoading = self.locate('scrShot-browser/customer_service.png', region=self.cfg['map_mode_region2'])

        if findcity and not findWorld:
            currentMapMode = "city"
        elif findWorld and not findcity:
            currentMapMode = "world"
        elif findLoading:
            currentMapMode = "loading"
        else:
            currentMapMode = "nothing"
        
        # logger.info("当前界面的地图模式为： {}".format(currentMapMode))
        return currentMapMode
    def fullScreen(self,full=True):
        """让浏览器的界面全屏化，False 则退出全屏。"""
    
        # logger.info("开启全屏模式: {}。".format(full))    
        currentFullScreen = self.getFullScreen()
        
        if full and currentFullScreen:
            # logger.info("已经在全屏状态下了。")
            return True
        
        #定位到全屏/正常化 按钮的位置
        searchRegion = self.cfg['full_region']
        findPos  = self.locateCenter( "./scrShot-browser/fullScreenButton.png", region=searchRegion)
        findPos2 = self.locateCenter( "./scrShot-browser/fullScreenButton2.png", region=searchRegion)
        
        findPos = findPos or findPos2
        if findPos and (full != currentFullScreen ):
            self.click(findPos.x, findPos.y)
            pyautogui.press('space')     # press the F1 key
            return True
        # logger.warning("匹配不到全屏按钮的位置，无法设置全屏模式。")
        return False
    def setWindowFocus(self):
        """设置主窗口获得焦点"""
        taskBarIconBrowser = self.cfg['taskbariconbrowser']
        self.click(taskBarIconBrowser)
        browserGotTab = self.cfg['browsergottab']
        self.click(browserGotTab)
        return True
    def setTopView(self):
        """set the view is far away."""
        # self.click(self.SCREEN_MID_W, self.SCREEN_MID_H, clicks=1, button="right")
        self.press("D")
        self.sleep(4)
        self.press("S")
        # closeActiveWindow()
        # 保证窗口界面是最远处视图
        # logger.info("设置界面为最远视图。")
        self.scroll(-20)
        self.sleep(2)

        self.scroll(-20)
        self.sleep(2)

    def restartGame(self):
        '''# 重新刷新当前页面，然后设置全屏最大化，最远视图模式'''
        # logger.info("利用浏览器的快捷键[F5]刷新页面。")
        pyautogui.press("F5")
        pyautogui.sleep(10)

        #waitting
        timeBegin = time.time()
        # logger.info("开始等待页面加载。。。 开始时间:{:.2f}".format(timeBegin))
        while True:
            #check the startup login screen
            # 判断是否世界模式
            loginSuccess = self.locateCenter('scrShot-browser/loginScreen.png')
            timeEnd = time.time()
            if loginSuccess:
                # logger.info("加载页面完成. 总共耗时 {:.2f} 秒。".format(timeEnd - timeBegin))
                self.click(loginSuccess, button="right")
                self.closeActiveWindow()
                self.fullScreen(True)
                # 保证窗口界面是最远处视图
                setTopView()
                # break
                return True
            
            logger.info("继续等待页面加载中......耗费的时间:{:.2f}秒".format(timeEnd - timeBegin))
            
            #time out        
            if  timeEnd - timeBegin > Action.timeout:
                logger.info("页面加载超时. 耗费的时间超过设定的【{:.2f}】秒。".format(Action.timeout))
                break
            
            # pyautogui.sleep(1)
            setPause(5)
        
        return False
    def closeActiveWindow(self):
        """关闭当前界面所有活动窗口"""
        # logger.info("利用快捷键[S]进行关闭窗口。")
        
        start = round(time.time())

        # 关闭所有带 X 图标的窗口
        searchRegion = self.cfg['close_region']
        while True:
            pyautogui.press("S")

            findPosCenter = pyautogui.locateCenterOnScreen("scrShot/buttonClose.png", confidence=0.9, region=searchRegion)
            if findPosCenter:
                # logger.info("找到有窗口需要进行关闭，窗口关闭按钮1【X】坐标位置x={},y={}".format(findPosCenter.x, findPosCenter.y))
                self.click(findPosCenter.x, findPosCenter.y)

            findPosCenter2 = pyautogui.locateCenterOnScreen("scrShot/buttonClose2.png", confidence=0.9, region=searchRegion)        
            if findPosCenter2:
                # logger.info("找到有窗口需要进行关闭，窗口关闭按钮2【X】坐标位置x={},y={}".format(findPosCenter2.x, findPosCenter2.y))
                self.click(findPosCenter2.x, findPosCenter2.y)
            
            findPosCenter3 = pyautogui.locateCenterOnScreen("scrShot-browser/buttonClose3.png", confidence=0.9, region=searchRegion)        
            if findPosCenter3:
                # logger.info("找到有窗口需要进行关闭，窗口关闭按钮3【X】坐标位置x={},y={}".format(findPosCenter3.x, findPosCenter3.y))
                self.click(findPosCenter3)
            
            findPosCenter4 = pyautogui.locateCenterOnScreen("scrShot-browser/buttonClose4.png", confidence=0.9, region=searchRegion)        
            if findPosCenter4:
                # logger.info("找到有窗口需要进行关闭，窗口关闭按钮4【X】坐标位置x={},y={}".format(findPosCenter4.x, findPosCenter4.y))
                self.click(findPosCenter4)

            if not findPosCenter and not findPosCenter2 and not findPosCenter3 and not findPosCenter4:
                # logger.info("未找到有活动窗口。")
                break

            if round(time.time()) - start > Action.timeout:
                # logger.info("关闭活动窗口超时。")
                restartGame()
                break
class MerchantShip(Action):
    pass

if __name__ == "__main__":
    Action.getGlobalConfig()
    # ship = MerchantShip()
    # print(ship.cfg["ship"])
    screen =ScreenWindow()
    # print(screen.getFullScreen())
    screen.setWindowFocus()
    screen.closeActiveWindow()
    screen.fullScreen()
    print(screen.getMapMode())
    screen.setTopView()
    # cfg = GotConfig()
    # cfg.read("config.v3.ini")
    # value = cfg.getIntTuple("ScreenWindow","searchRegion")
    # print(value)
    # value = cfg.getStrList("ScreenWindow","searchRegion")
    # print(value)
    # value = cfg.getint("Global","timeout")
    # print(value)